贪心算法实例(七):最小生成树Kruskal

     什么是最小生成树?
    生成树是相对图来说的,一个图的生成树是一个树并把图的所有顶点连接在一起。一个图可以有许多不同的生成树。一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。最小生成树其实是最小权重生成树的简称。生成树的权重是考虑到了生成树的每条边的权重的总和。

    最小生成树有几条边?
    最小生成树有(V – 1)条边,其中V是给定的图的顶点数量。

    Kruskal算法
   下面是步骤寻找MST使用Kruskal算法:
    1,按照所有边的权重排序(从小到大)
    2,选择最小的边。检查它是否形成与当前生成树形成环。如果没有形成环,讲这条边加入生成树。否则,丢弃它。 

    3,重复第2步,直到有生成树(V-1)条边

具体实例及实现代码如下所示:

/**
 * @Title: Kruskal.java
 * @Package greedyalgorithm
 * @Description: TODO
 * @author peidong
 * @date 2017-6-1 上午9:34:10 
 * @version V1.0
 */
package greedyalgorithm;

import java.util.Collections;
import java.util.LinkedList;

/**
 * @ClassName: Kruskal
 * @Description: 最小生成树Kruskal算法实现
 * @date 2017-6-1 上午9:34:10 
 *
 */

public class Kruskal {

    /**
     *
     * @ClassName: FastUnionFind
     * @Description:  由连通分支组成的集合为U,包括union(a,b);和find(v)的基本运算 
     * @date 2017-6-1 上午9:50:11 
     *
     */
    public static class FastUnionFind {
        public int[] u;  //数组用来保存顶点所属的集合,用数字表示
        public FastUnionFind(int n){
            u = new int[n + 1];
            for(int i = 1; i <= n; i++){ //初始化顶点数组值
                u[i] = i;
            }
        }
        public int find(int x){ //找到顶点所属的集合
            return u[x];
        }

        public void union(int x, int y){ //将第二个顶点归入第一个顶点集合
            u[y] = u[x];
        }
    }

    public static class EdgeNode implements Comparable{
        float weight; //边权值
        int  u, v; //边的左右顶点

        /**
         *
         * <p>Title: </p> 
         * <p>Description:构造函数 </p> 
         * @param u
         * @param v
         * @param weight
         */
        public EdgeNode(int u, int v, float weight){
            this.u = u;
            this.v = v;
            this.weight = weight;
        }

        /**
         * 按照边的权值升序排列
         */
        public int compareTo(Object x){
            float xw = ((EdgeNode)x).weight;
            if(weight < xw)
                return -1;
            if(weight == xw)
                return 0;
            return 1;
        }
    }

    /**
     *
     * @Title: kruskal
     * @Description: kruskal算法
     * @param n 所有顶点的数目
     * @param E 边的集合(所有的边)
     * @param t 保存逐步连通的边
     * @return  是否生成了最小生成树
     * @return boolean
     * @throws
     */
    public static boolean kruskal(int n, LinkedList<EdgeNode> E, EdgeNode[] t){
        FastUnionFind U = new FastUnionFind(n);
        int k = 0;
        while(k < n-1){  //n个顶点,n-1条边
            EdgeNode x = E.peek();
            int a = U.find(x.u);  //边的左顶点所属的集合
            int b = U.find(x.v);  //边的右顶点所属集合
            if(a != b){
                t[k++] = x;
                U.union(a, b);
            }
            E.pop();
        }
        for(int i = 0; i < k; i++){
            System.out.println("左顶点:"+t[i].u+"; 右顶点:"+t[i].v+"; 长度:"+t[i].weight);
        }
        return (k == n-1);
    }
    /**
     * @Title: main
     * @Description: TODO
     * @param args
     * @return void
     * @throws
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        int n=6;
        EdgeNode e1=new EdgeNode(1,2,6);
        EdgeNode e2=new EdgeNode(1,3,1);
        EdgeNode e3=new EdgeNode(1,4,5);
        EdgeNode e4=new EdgeNode(2,3,5);
        EdgeNode e5=new EdgeNode(3,4,5);
        EdgeNode e6=new EdgeNode(2,5,3);
        EdgeNode e7=new EdgeNode(3,5,6);
        EdgeNode e8=new EdgeNode(5,6,6);
        EdgeNode e9=new EdgeNode(3,6,4);
        EdgeNode e10=new EdgeNode(4,6,2);
        LinkedList<EdgeNode> E=new LinkedList<EdgeNode>();
        E.add(e10);
        E.add(e9);
        E.add(e8);
        E.add(e7);
        E.add(e6);
        E.add(e5);
        E.add(e4);
        E.add(e3);
        E.add(e2);
        E.add(e1);
        Collections.sort(E);
        EdgeNode[] t=new EdgeNode[n];
        kruskal(n,E,t);
    }
}

//时间复杂度:
//
//O(ElogE) 或 O(ElogV)。 排序使用 O(ELogE) 的时间,之后我们遍历中使用并查集O(LogV) ,所以总共复杂度是 O(ELogE + ELogV)。E的值最多为V^2,所以
//
//O(LogV) 和 O(LogE) 可以看做是一样的。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值